﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
//函数模板特化：模板中的代码都是一成不变的，但对于某些特殊类型，
//可能仍需调用该函数，但逻辑却大不相同/或是有微小差距，但这些情况都不能直接使用函数模板，
//举例来说，对于可以正常比较大小的类型，直接调用比较运算符即可，但指针却要先解引用再比较
//这时，就需要用到模板特化或函数重载来解决了
//注：能使用函数重载解决该问题的原因，当存在同名函数与函数模板时，由于模板还需要实例化才能使用，
	//编译器会在此时默认调用已经写好了的函数重载
//注：函数模板只有全特化，没有半特化

//函数模板特化应用举例：比较大小的函数
template<class T>
bool func(const T& a, const T& b)
{
	cout << "bool func(const T& a, const T& b)" << endl;
	return a < b;
}

template<>//函数模板特化的语法就是template<>+特化的函数
//需要注意的是，函数模板中函数的参数为const T& a，这里的const是修饰的a，
//如果将a换做指针类型，就要使a不能被修改（顶层const），因此，
//特化的const不应和模板一样加在类型名前（底层const），而应加载类型名后（顶层const）
bool func<int*>(int* const & pa, int* const & pb)
//bool func(int* const& a, int* const& b)//<int*>语法上可加可不加，但从代码角度推荐加上
{
	cout << "bool func<int*>(int* const & pa, int* const & pb)" << endl;
	return *pa < *pb;
}

//函数重载就不需要估计函数模板的参数类型了，const可加可不加，相比特化显得更加灵活
bool func(int* pa, int* pb)
{
	cout << "bool func(int* pa, int* pb)" << endl;
	return *pa < *pb;
}

void test1()
{
	//可比较类型：
	int a = 3, b = 1;
	if (func(a, b))//打印a，b中的小值
	{
		cout << a << endl;
	}
	else {
		cout << b << endl;
	}

	//指针类型
	int* pa = &a, * pb = &b;
	cout << pa << endl << pb << endl;//虽然a的值比b大，但b地址比a大
	if (func(pa, pb))//因此，结果打印出了地址小的a，而非值小的b
	{
		cout << *pa << endl;
	}
	else {
		cout << *pb << endl;
	}
	//为了解决这种问题，可以采用函数模板特化->20行
	//但同时，也可以使用函数重载来解决这个问题->32行

	//注：运行发现，当特化和函数重载同时存在时，会调用函数重载，而非特化
}

//函数模板只有全特化，不支持半特化，但支持函数模板重载
template<class T1, class T2>
void funcs(const T1& a, const T2& b)
{
	cout << "void funcs(const T1& a, const T2& b)" << endl;
}

////尝试半特化funcs函数：
//template<class T1>
//void funcs<T1, int>(const T1& a, const int& b)//非法使用显示模板参数->失败
//{
//	cout << "void funcs(const T1& a, const int& b)" << endl;
//}

//虽然C++明确规定不支持半特化函数模板，但却可以支持函数模板重载，
//它可以完美替代不被允许的半特化函数模板的所有功能
//funcs的函数模板重载：
template<class T1>
void funcs(const T1& a, const int& b)
{
	cout << "void funcs(const T1& a, const int& b)" << endl;
}

void test2()
{
	funcs(1, 1.1);//void funcs(const T1& a, const T2& b)\n
	funcs(1, 1);//void funcs(const T1& a, const int& b)\n
}
int main()
{
	//test1();
	test2();
	return 0;
}

//延伸总结：​​当以下四种同名函数同时出现时，会依照如下顺序进行调用
//1.普通函数（非模板，完全匹配）
//2.通用函数模板的模板重载（另一个函数模板，匹配当前类型）
//3.函数模板特化（针对特定类型的特化版本）
//4.通用函数模板（最通用的情况，需要实例化）